#include <iostream>
#include <string>
#include <memory>
/*
    简单工厂模式: 可以很好的将类对象的创建和使用进行分离，解耦合；但增加新的类创建方法时违反开闭原则
*/

// 1. 简单工厂模式
// class Fruit
// {
// public:
//     virtual void Name() = 0;
// };
//
// class Apple : public Fruit
// {
// public:
//     void Name() override
//     {
//         std::cout << "我是一个苹果!!" << std::endl;
//     }
// };
//
// class Banana : public Fruit
// {
// public:
//     void Name() override
//     {
//         std::cout << "我是一个香蕉!!" << std::endl;
//     }
// };
//
// class FruitFactory
// {
// public:
//     static std::shared_ptr<Fruit> CreateFruit(const std::string name)
//     {
//         if (name == "Apple")
//             return std::make_shared<Apple>();
//         else if (name == "Banana")
//             return std::make_shared<Banana>();
//         return nullptr;
//     }
// };

// 2. ⼯⼚⽅法模式
// class Fruit
// {
// public:
//     virtual void Name() = 0;
// };

// class Apple : public Fruit
// {
// public:
//     void Name() override
//     {
//         std::cout << "我是一个苹果!!" << std::endl;
//     }
// };

// class Banana : public Fruit
// {
// public:
//     void Name() override
//     {
//         std::cout << "我是一个香蕉!!" << std::endl;
//     }
// };

// class FruitFactory
// {
// public:
//     virtual std::shared_ptr<Fruit> CreateFactory() = 0;
// };

// class AppleFactory : public FruitFactory
// {
// public:
//     std::shared_ptr<Fruit> CreateFactory() override
//     {
//         return std::make_shared<Apple>();
//     }
// };

// class BananaFactory : public FruitFactory
// {
// public:
//     std::shared_ptr<Fruit> CreateFactory() override
//     {
//         return std::make_shared<Banana>();
//     }
// };

// 3.抽象⼯⼚模式
class Fruit
{
public:
    virtual void Name() = 0;
};

class Apple : public Fruit
{
public:
    virtual void Name() override
    {
        std::cout << "我是一个苹果!!" << std::endl;
    }
};

class Banana : public Fruit
{
public:
    virtual void Name() override
    {
        std::cout << "我是一个香蕉!!" << std::endl;
    }
};

class Animal
{
public:
    virtual void name() = 0;
};

class Lamp : public Animal
{
public:
    virtual void name() override
    {
        std::cout << "我是一只山羊!!" << std::endl;
    }
};

class Dog : public Animal
{
public:
    virtual void name() override
    {
        std::cout << "我是一只土狗!!" << std::endl;
    }
};

class Factory
{
public:
    virtual std::shared_ptr<Fruit> getFruit(const std::string &name) = 0;
    virtual std::shared_ptr<Animal> getAnimal(const std::string &name) = 0;
};

class FruitFactory : public Factory
{
public:
    virtual std::shared_ptr<Fruit> getFruit(const std::string &name) override
    {
        if (name == "Apple")
            return std::make_shared<Apple>();
        else
            return std::make_shared<Banana>();
    }
    virtual std::shared_ptr<Animal> getAnimal(const std::string &name) override
    {
        return std::shared_ptr<Animal>();
    }
};

class AnimalFactory : public Factory
{
public:
    virtual std::shared_ptr<Animal> getAnimal(const std::string &name) override
    {
        if (name == "Lamp")
            return std::make_shared<Lamp>();
        else
            return std::make_shared<Dog>();
    }
    virtual std::shared_ptr<Fruit> getFruit(const std::string &name) override
    {
        return std::shared_ptr<Fruit>();
    }
};

int main()
{
    std::shared_ptr<Factory> ff(new FruitFactory);
    std::shared_ptr<Fruit> fruit = ff->getFruit("Apple");
    fruit->Name();
    fruit = ff->getFruit("Banana");
    fruit->Name();

    std::shared_ptr<Factory> Aml(new AnimalFactory);
    std::shared_ptr<Animal> animal = Aml->getAnimal("Lamp");
    animal->name();
    animal = Aml->getAnimal("Dog");
    animal->name();
    // std::shared_ptr<FruitFactory> ff(new AppleFactory);
    // std::shared_ptr<Fruit> a = ff->CreateFactory();
    // a->Name();

    // ff.reset(new BananaFactory);
    // a = ff->CreateFactory();
    // a->Name();

    // FruitFactory::CreateFruit("Banana")->Name();
    // FruitFactory::CreateFruit("Apple")->Name();
    // FruitFactory::CreateFruit("Banana")->Name();
    return 0;
}